//    Copyright (C) Kherty.  All rights reserved.
using System;

namespace OpenLS.Spreadsheet.StandardAddIn.Statistics
{
    internal class SolverUtils
    {
        public static double[] bracket(DoubleFunction function,
                                       double initial, double lowerBound, double upperBound)
        {
            return bracket(function, initial, lowerBound, upperBound,
                           int.MaxValue);
        }

        public static double[] bracket(DoubleFunction function,
                                       double initial, double lowerBound, double upperBound,
                                       int maximumIterations)
        {
            if (function == null)
            {
                throw new ArgumentOutOfRangeException("function is null.");
            }
            if (maximumIterations <= 0)
            {
                throw new ArgumentOutOfRangeException
                    ("bad value for maximumIterations: " + maximumIterations);
            }
            if (initial < lowerBound || initial > upperBound || lowerBound >= upperBound)
            {
                throw new ArgumentOutOfRangeException
                    ("Invalid endpoint parameters:  lowerBound=" + lowerBound +
                     " initial=" + initial + " upperBound=" + upperBound);
            }
            double a = initial;
            double b = initial;
            double fa;
            double fb;
            int numIterations = 0;

            double step = 0.0001;
            do
            {
                a = Math.Max(a - step, lowerBound);
                b = Math.Min(b + step, upperBound);
                fa = function(a);

                fb = function(b);
                numIterations++;
                step = step*2;
            } while ((fa*fb > 0.0) && (numIterations < maximumIterations) &&
                     ((a > lowerBound) || (b < upperBound)));

            if (fa*fb > 0.0)
            {
                throw new ConvergenceException();
            }

            return new double[] {a, b};
        }


        private static Solver newDefaultSolver(DoubleFunction f)
        {
            return newBrentSolver(f);
        }

        private static Solver newBrentSolver(DoubleFunction f)
        {
            return new BrentSolver(f);
        }

        public static double Solve(DoubleFunction f, double x0, double x1,
                                   double absoluteAccuracy)
        {
            Solver solver = newDefaultSolver(f);
            solver.AbsoluteAccuracy = absoluteAccuracy;
            return solver.Solve(x0, x1);
        }
    }
}